#include <asm/io.h>
#include <asm/regs.h>
#include <asm/system.h>
#include <cnix/wait.h>
#include <cnix/partition.h>
#include <cnix/fs.h>
#include <cnix/kernel.h>
#include <cnix/driver.h>
#include <cnix/list.h>
#include <cnix/string.h>
#include <cnix/cdrom.h>
#include <cnix/mm.h>

#define ide_io_read(port,buf,nr) \
	do{ \
		long _v; \
		__asm__ volatile( \
			"cld;rep;insw" \
			:"=D"(_v) \
			:"d"(port),"0"(buf),"c"(nr) \
			); \
	}while(0)

#define ide_io_write(port,buf,nr) \
	do{ \
		long _v; \
		__asm__ volatile( \
			"cld;rep;outsw" \
			:"=S"(_v) \
			:"d"(port),"0"(buf),"c"(nr) \
			); \
	}while(0)

static unsigned int cyl;
static unsigned char head;
static unsigned int sec;
static unsigned int precomp;

#define MAX_ERRS	3
static int need_reset = 0, errs = 0;

static struct hd_info{
	int flag;
	unsigned int start_sec;
	unsigned int num_sec;
}hd_info[5];

static struct cdrom_info{
	char valid;
	char reset;
	struct iso9660_super_block iso9660;
}cdrom_info[2];

#define ATA 0
#define ATAPI 1

static struct request{
	char type; // ATA, ATAPI
	int sec;
	int nsec;
	int rw_mode;
	list_t list;
	struct buf_head * buf;
}reqq[REQQ_SIZE];

static struct request * cur_req = NULL;
static list_t req_freelist;
static struct wait_queue * req_wait = NULL;

static int dma_ide_flag = 0;
static unsigned short dma_base_addr;

#define DMA_COMMAND_REG		0
#define DMA_STATUS_REG		2
#define DMA_PRD_ADDR_REG	4

// physical_region_descriptor
typedef struct { 
	unsigned long addr;
	unsigned long len; // EOT ... len, last one if EOT is 1
}ide_prd_t;

static ide_prd_t * ide_prd;
static unsigned char * ide_dma_buffer = NULL;

static int ide_waitfor(int, int);
static int finish_cmd(void);
static int ide_reset(void);
static void ide_status_ok(void);
static int free_cur_req(void);
static void ide_intr(struct regs_t *);
static BOOLEAN ide_cmd(int, int, unsigned int, unsigned int, BOOLEAN);
static BOOLEAN dma_ide_cmd(int, int, unsigned int, unsigned int, BOOLEAN);
static struct request * get_req(void);

void ide_init(void);

static int ide_waitfor(int mask, int value)
{
	long flags;
	mstate_t ms;

	lock(flags);

	mstart(&ms);
	do{
		if((inb(IDE_STATUS) & mask) == value){
			unlock(flags);
			return 1;
		}
	}while(melapsed(&ms) < TIMEOUT);

	need_reset = 1;

	unlock(flags);

	return 0;
}

static int ide_reset(void)
{
	long flags;

	lock(flags);

	mdelay(500);

	outb(RESET_CTL, IDE_CTL);
	mdelay(50);
	outb(0, IDE_CTL);
	mdelay(50);

	unlock(flags);

	if(!ide_waitfor(BUSY_STAT | READY_STAT, READY_STAT))
		return 0;
	
	lock(flags);

	need_reset = 0;

	unlock(flags);

	return 1;
}

static void ide_status_ok(void)
{
	long flags;

	lock(flags);

	while(inb(IDE_STATUS) & BUSY_STAT)
		mdelay(50);

	unlock(flags);
}

static BOOLEAN ide_cmd0(
	int drive, int cmd, unsigned int sec, unsigned int nsec,
	BOOLEAN disable
	)
{
	int c, h, s;
	unsigned char ctl;

	s = sec & 0xff;
	c = (sec & 0xffff00) >> 8;
	h = (sec & 0xf000000) >> 24;

	if((inb(IDE_STATUS) & BUSY_STAT) != 0 || !ide_waitfor(BUSY_STAT, 0)){
		printk("ide controller is not ready!\n");
		return FALSE;
	}

	outb(0xe0 | (drive << 4) | h, IDE_CURRENT);

	if((inb(IDE_STATUS) & BUSY_STAT) != 0 || !ide_waitfor(BUSY_STAT, 0)){
		printk("ide controller is not ready!\n");
		return FALSE;
	}

	ctl = 0;
	if(disable)
		ctl |= DISIRQ_CTL;

	if(head >= 8)
		ctl |= EIGHTHEADS_CTL;

	outb(ctl, IDE_CTL);
	outb(precomp, IDE_PRECOMP);

     	outb(nsec, IDE_NSECTOR);
	outb(s, IDE_SECTOR);
	outb(c, IDE_LCYL);
	outb(c >> 8, IDE_HCYL);
	
	outb(cmd, IDE_CMD);

	return TRUE;
}

static BOOLEAN ide_cmd(
	int drive, int cmd, unsigned int sec, unsigned int nsec,
	BOOLEAN disable
	)
{
	BOOLEAN ret;
	unsigned long flags;

	lock(flags);

	ret = ide_cmd0(drive, cmd, sec, nsec, disable);

	if(cmd == WRITE_CMD){
		while((inb(IDE_STATUS) & DRQ_STAT) != DRQ_STAT)
			printk("looping\n");
	}

	unlock(flags);

	return ret;
}

static BOOLEAN dma_ide_cmd(
	int drive, int cmd, unsigned int sec, unsigned int nsec,
	BOOLEAN disable
	)
{
	unsigned char dmacmd;
	unsigned long flags;

	lock(flags);

	// bit2: r/w, bit1: reserved, bit0: start/stop
	outb(0x00, dma_base_addr + DMA_COMMAND_REG);

	// clear interrupt and error
	outb(0x06, dma_base_addr + DMA_STATUS_REG); 

	ide_prd->addr = (unsigned long)ide_dma_buffer;
	ide_prd->len = (512 * nsec) | 0x80000000; // EOT = 1

	outl((unsigned long)ide_prd, dma_base_addr + DMA_PRD_ADDR_REG);

	dmacmd = (cmd == DMA_READ_CMD) ? 0x08 : 0; 
	outb(dmacmd, dma_base_addr + DMA_COMMAND_REG);

	if(!ide_cmd0(drive, cmd, sec, nsec, disable)){
		// clear interrupt
		outb(0x04, dma_base_addr + DMA_STATUS_REG);
		inb(dma_base_addr + DMA_STATUS_REG);

		// stop
		outb(0x00, dma_base_addr + DMA_COMMAND_REG);

		unlock(flags);

		return FALSE;
	}

	if(cmd == DMA_WRITE_CMD){
		while((inb(IDE_STATUS) & DRQ_STAT) != DRQ_STAT)
			printk("looping\n");
	}

	inb(dma_base_addr + DMA_COMMAND_REG);
	inb(dma_base_addr + DMA_STATUS_REG);

	outb(dmacmd | 0x01, dma_base_addr + DMA_COMMAND_REG);

	unlock(flags);

	return TRUE;
}

static void cdrom_status_ok(void)
{
	long flags;

	lock(flags);

	while(inb(IDE_STATUS) & BUSY_STAT)
		mdelay(50);

	unlock(flags);
}

static BOOLEAN cdrom_ready(void)
{
	int times;
	long flags;
	unsigned char status;

	times = 0;

	lock(flags);

	do{
		status = inb(IDE_STATUS);
		if(status & ERR_STAT){
			unlock(flags);
			return FALSE;
		}

		mdelay(50);

		times++;
		if(times >= 3){ // xxx
			unlock(flags);
			return FALSE;
		}
	}while(!(status & READY_STAT));

	unlock(flags);

	return TRUE;
}

static BOOLEAN atapi_cmd(int drive, unsigned short * data, BOOLEAN disable)
{
	unsigned char ctl;

	if((inb(IDE_STATUS) & BUSY_STAT) != 0 || !ide_waitfor(BUSY_STAT, 0)){
		printk("ide controller is not ready!\n");
		return FALSE;
	}

	outb(0xe0 | (drive << 4), IDE_CURRENT); // master or slave
	outb(0x0, IDE_ERROR); // not DMA

	outb(0x00, IDE_LCYL);
	outb(0x08, IDE_HCYL); // 2048 bytes

	ctl = 0;
	if(disable)
		ctl |= DISIRQ_CTL;

	outb(ctl, IDE_CTL); // nIEN

	outb(0xa0, IDE_STATUS); // packet command

	if((inb(IDE_STATUS) & BUSY_STAT) != 0 || !ide_waitfor(BUSY_STAT, 0)){
		printk("ide controller is not ready!\n");
		return FALSE;
	}

	if(!cdrom_ready())
		return FALSE;

	outw_p(data[0], IDE_DATA);
	outw_p(data[1], IDE_DATA);
	outw_p(data[2], IDE_DATA);
	outw_p(data[3], IDE_DATA);
	outw_p(data[4], IDE_DATA);
	outw_p(data[5], IDE_DATA);

	return TRUE;
}

static BOOLEAN atapi_rw_cmd(
	int drive, int cmd, unsigned int block, unsigned int length,
	BOOLEAN disable
	)
{
	unsigned char packet[12];

	memset(packet, 0, 12);

	packet[0] = cmd & 0xff;

	packet[2] = (block >> 24) & 0xff;
	packet[3] = (block >> 16) & 0xff;
	packet[4] = (block >> 8) & 0xff;
	packet[5] = block & 0xff;

	packet[7] = (length >> 8) & 0xff;
	packet[8] = length & 0xff;

	if(!atapi_cmd(drive, (unsigned short *)packet, disable))
		return FALSE;

	return TRUE;
}

static int free_cur_req(void)
{
	struct request * tmp;

	if(list_empty(&req_freelist))
		wakeup(&req_wait);

	tmp = cur_req;

	if(list_empty(&cur_req->list)){
		cur_req = NULL;

		list_add_head(&req_freelist, &tmp->list);
		return 0; // nothing to do
	}

	cur_req = list_entry(cur_req->list.next, list, struct request);

	list_del1(&tmp->list);
	list_add_head(&req_freelist, &tmp->list);

	return 1; // more work to do
}

static void ide_intr(struct regs_t * regs)
{
	raise_bottom(IDE_B);
}

static void harddisk_bottom(void)
{
	long flags;

	if(dma_ide_flag){
		lock(flags);

		// clear interrupt
		outb(0x04, dma_base_addr + DMA_STATUS_REG);
		inb(dma_base_addr + DMA_STATUS_REG);

		// stop
		outb(0x00, dma_base_addr + DMA_COMMAND_REG);

		unlock(flags);

		if(cur_req->rw_mode == READ)
			memcpy(cur_req->buf->b_data, ide_dma_buffer, 1024);

		cur_req->nsec = 0;
	}else{
		unsigned char * data = cur_req->buf->b_data;

		lock(flags);

		while(inb(IDE_STATUS) & BUSY_STAT)
			printk("looping\n");

		if(cur_req->rw_mode == READ){
			int offset = 0;

			if(cur_req->nsec < 2)
				offset = 512;

			ide_io_read(IDE_DATA, &data[offset], 256);
		}else{ // WRITE 
			if(cur_req->nsec > 1)
				ide_io_write(IDE_DATA, &data[512], 256);
		}

		unlock(flags);

		cur_req->nsec--;
	}
}

void ide_bottom(void)
{

	if(!cur_req){
		PRINTK("interrupt without request\n");
		return;
	}

	switch(cur_req->type){
	case ATA:
		harddisk_bottom();
		break;
	case ATAPI:
		if(cur_req->rw_mode != READ)
			DIE("BUG: cannot happen\n");

		ide_io_read(IDE_DATA, cur_req->buf->b_data, 1024);

		if(inb(IDE_STATUS) & ERR_STAT){
			cur_req->buf->b_flags |= B_ERROR;
			cdrom_info[MINOR(cur_req->buf->b_dev)].reset = 1;
		}

		cur_req->nsec = 0;
		break;
	default:
		DIE("BUG: cannot happen\n");
		break;
	}

	if(cur_req->nsec > 0)
		return;

	iodone(cur_req->buf);

	if(!free_cur_req())
		return;

	finish_cmd();
}

static int finish_cmd(void)
{
	int d = 0;
	unsigned char cmd = 0;
	BOOLEAN (*ide_cmd_func)(int, int, unsigned int, unsigned int, BOOLEAN);

	ide_cmd_func = ide_cmd;

	switch(cur_req->rw_mode){
	case READ:
		switch(cur_req->type){
		case ATA:
			cmd = READ_CMD;
			if(dma_ide_flag){
				cmd = DMA_READ_CMD;
				ide_cmd_func = dma_ide_cmd;	
			}

			break;
		case ATAPI:
			if(cdrom_info[MINOR(cur_req->buf->b_dev)].reset){
				cdrom_info[MINOR(cur_req->buf->b_dev)].reset
					= 0;

				// xxx, don't work
				ide_reset();
			}

			cmd = ATAPI_READ;
			d = 1;
			ide_cmd_func = atapi_rw_cmd;
			break;
		default:
			DIE("BUG: cannot happen\n");
			break;
		}

		if(!ide_cmd_func(d, cmd, cur_req->sec, cur_req->nsec, FALSE)){
			errs++;
			while(errs < MAX_ERRS){
				if(need_reset)
					if(!ide_reset())
						DIE("ide_reset is useless!\n");

				if(ide_cmd_func(d, cmd,
					cur_req->sec, cur_req->nsec, FALSE))

					break;

				errs++;
			}

			if(errs == MAX_ERRS){
				printk("So many times for error!\n");
				cur_req->buf->b_flags |= B_ERROR;

				return 0;	
			}

			errs = 0;
		}

		break;
	case WRITE:
		if(cur_req->type != ATA)
			DIE("BUG: cannot happen\n");

		cmd = WRITE_CMD;

		if(dma_ide_flag){	
			cmd = DMA_WRITE_CMD;
			ide_cmd_func = dma_ide_cmd;
			memcpy(ide_dma_buffer, cur_req->buf->b_data, 1024);
		}

		if(!ide_cmd_func(d, cmd, cur_req->sec, cur_req->nsec, FALSE)){
			errs++;
			while(errs < MAX_ERRS){
				if(need_reset)
					if(!ide_reset())
						DIE("ide_reset is useless!\n");
				if(ide_cmd_func(d, cmd,
					cur_req->sec, cur_req->nsec, FALSE))
					break;

				errs++;
			}

			if(errs == MAX_ERRS){
				printk("So many times for error!\n");
				cur_req->buf->b_flags |= B_ERROR;

				return 0;	
			}

			errs = 0;
		}

		if(!dma_ide_flag){
			unsigned long flags;

			lock(flags);
			ide_io_write(IDE_DATA, cur_req->buf->b_data, 256);
			unlock(flags);
		}

		break;
	default:
		DIE("BUG: cannot happen\n");
		break;
	}

	return 1;	
}

static struct request * get_req(void)
{
	unsigned long flags;
	struct request * req;

	lockb_ide(flags);

try_again:
	if(!list_empty(&req_freelist)){
		req = list_entry(req_freelist.next, list, struct request);
		list_del1(&req->list);
	}else{ // wake up in ide_bottom
		sleepon(&req_wait);
		goto try_again;
	}
		
	unlockb_ide(flags);

	return req;
}

BOOLEAN atapi_sense_key(int drive, unsigned char data[32])
{
	unsigned char packet[12];

	memset(packet, 0, 12);

	packet[0] = ATAPI_SENSE_KEY;
	packet[4] = 32;

	atapi_cmd(drive, (unsigned short *)packet, TRUE);

	cdrom_status_ok();
	if(!cdrom_ready())
		return FALSE;

	ide_io_read(IDE_DATA, data, 16);

	return TRUE;
}

static BOOLEAN atapi_inquiry(int drive, unsigned char data[128])
{
	unsigned char packet[12];

	memset(packet, 0, 12);

	packet[0] = ATAPI_INQUIRY;
	packet[4] = 128;

	atapi_cmd(drive, (unsigned short *)packet, TRUE);

	cdrom_status_ok();

	// not reporting ready for inquiry
	//if(!cdrom_ready())
	//	return FALSE;

	ide_io_read(IDE_DATA, data, 64);

	return TRUE;
}

static BOOLEAN ide_cdrom_inquiry(dev_t minor)
{
	unsigned char data[128];
	char vendor[9], production[17], revision[5];

	if(!atapi_inquiry(1, data))
		return FALSE;

	// peripheral device type
	if((data[0] & 31) != ATAPI_CDROM)
		return FALSE;

	memcpy(vendor, &data[8], 8);
	vendor[8] = '\0';

	memcpy(production, &data[16], 16);
	production[16] = '\0';

	memcpy(revision, &data[32], 4);
	revision[4] = '\0';

	printk("\nCDROM %s-%s from %s\n", production, revision, vendor);

	return TRUE;
}

BOOLEAN ide_cdrom_open(dev_t minor, void * data)
{
	int i;
	unsigned long sum;
	struct buf_head * bh;
	struct iso_primary_descriptor * pvd;
	char standard[6];
	struct iso9660_super_block * iso9660;

	if((minor >= 2) && !cdrom_info[minor].valid)
		return FALSE;

	if(!ide_cdrom_inquiry(minor))
		return FALSE;

	// xxx: first invalidate it
	inval_block(DEVNUM(CDROM_MJ_DEV, minor), PRIMARY_DESC_BLOCK);

	bh = bread(DEVNUM(CDROM_MJ_DEV, minor), PRIMARY_DESC_BLOCK);
	if(!bh)
		return FALSE;

	pvd = (struct iso_primary_descriptor *)bh->b_data;
	if(pvd->type == PVD_DESC){
		memcpy(standard, pvd->id, 5);
		standard[5] = '\0';

		if(strcmp(standard, "CD001")){ // not iso9660-compliant
			brelse(bh);
			return FALSE;
		}

		iso9660 = (struct iso9660_super_block *)data;

		sum = 0;
		for(i = 0; i < CDROM_BLOCKSIZ; i += 4)
			sum += *(unsigned long *)&bh->b_data[i];	

		iso9660->lbs = pvd->logical_block_size;
		if(iso9660->lbs != CDROM_BLOCKSIZ){
			brelse(bh);
			return FALSE;
		}

		iso9660->vss = pvd->volume_space_size;

		iso9660->path_table = pvd->type_l_path_table;
		iso9660->path_table_size = pvd->path_table_size;
		memcpy(
			&iso9660->root_directory_record,
			pvd->root_directory_record,
			sizeof(struct iso_directory_record)
			);

		if(iso9660->sum != sum)
			inval_dev(DEVNUM(CDROM_MJ_DEV, minor));

		iso9660->sum = sum;

		brelse(bh);

		if(iso9660 != &cdrom_info[minor].iso9660)
			memcpy(
				&cdrom_info[minor].iso9660,
				iso9660,
				sizeof(struct iso9660_super_block)
				);

		return TRUE;
	}

	brelse(bh);

	return FALSE;
}

int ide_open(dev_t major, dev_t minor, int flags)
{
	switch(major){
	case HD_MJ_DEV:	
		if(minor < 0 || minor > 4 || !hd_info[minor].flag)
			return -1;
		break;
	case CDROM_MJ_DEV:
		if((minor >= 2) && !cdrom_info[minor].valid)
			return 0;

		if(!ide_cdrom_open(minor, &cdrom_info[minor].iso9660))
			return -1;

		break;
	default:
		DIE("BUG: cannot happen\n");
		break;
	}

	return 0;
}

size_t ide_size(dev_t major, dev_t minor)
{
	switch(major){
	case HD_MJ_DEV:
		if(minor < 0 || minor > 4 || !hd_info[minor].flag){
			DIE("bad device ... %d!\n", minor);
			return 0;
		}

		return hd_info[minor].num_sec * 512;
	case CDROM_MJ_DEV:
		if((minor >= 2) && !cdrom_info[minor].valid){
			PRINTK("cdrom: bad device %d!\n", minor);
			return 0;
		}

		return cdrom_info[minor].iso9660.vss * CDROM_BLOCKSIZ;
	default:
		DIE("BUG: cannot happen\n");
		break;
	}

	return 0;
}

int ide_rw_block(int rw_mode, struct buf_head * buf)
{
	dev_t dev;
	char type = 0;
	int sec = 0, nsec = 0;
	unsigned long flags;
	struct request * req;

	dev = MINOR(buf->b_dev);

	switch(MAJOR(buf->b_dev)){
	case HD_MJ_DEV:
		type = ATA;
	
		if(dev < 0 || dev > 4 || !hd_info[dev].flag){
			buf->b_flags |= B_ERROR;
			PRINTK("harddisk: bad device %d\n", dev);
			return 0;
		}

		if((hd_info[dev].num_sec / 2 - 1) < buf->b_blocknr){
			buf->b_flags |= B_ERROR;
			PRINTK("harddisk: bad block %d\n", buf->b_blocknr);
			return 0;
		}

		sec = hd_info[dev].start_sec + buf->b_blocknr * 2;
		nsec = 2; // always 2

		break;
	case CDROM_MJ_DEV:
		type = ATAPI;

		if((dev >= 2) && !cdrom_info[dev].valid){
			PRINTK("cdrom: bad device %d!\n", dev);
			buf->b_flags |= B_ERROR;
			return 0;
		}

		if(buf->b_blocknr > PRIMARY_DESC_BLOCK){
			if(buf->b_blocknr >= cdrom_info[dev].iso9660.vss){
				buf->b_flags |= B_ERROR;
				PRINTK(
					"cdrom: bad block: %d\n",
					buf->b_blocknr
					);
				return 0;
			}
		}

		sec = buf->b_blocknr;
		nsec = 0x0001; // always 1

		break;
	default:
		DIE("BUG: cannot happen\n");
		break;
	}

	req = get_req();
	req->type = type;
	req->sec = sec;
	req->nsec = nsec;
	req->rw_mode = rw_mode;
	req->buf = buf;

	lockb_ide(flags);

	if(cur_req){
		if(list_empty(&cur_req->list))
			list_add_head(&cur_req->list, &req->list);
		else{
			list_t * tmp, * pos;
			struct request * prev, * next;

			prev = cur_req;

			foreach(tmp, pos, &cur_req->list)
				prev = list_entry(tmp, list, struct request);
				next = list_entry(tmp->next,
					list, struct request);

				if((prev->sec < req->sec)
					&& (next->sec >= req->sec))
					break;
			endeach(&cur_req->list);

			/* req after prev */
			list_add_head(&prev->list, &req->list);
		}

		unlockb_ide(flags);

		return 1;
	}

	cur_req = req;
	list_head_init(&cur_req->list);

	unlockb_ide(flags);

	return finish_cmd();
}

void get_ide_bios_info(void)
{
        unsigned short * ver_add;
	unsigned char * disk_add;
	unsigned int ver_cs, ver_offset, ver_line;

	/* bios C/H/S */
	ver_add = (unsigned short *)0x104;
	ver_offset = ver_add[0];
	ver_cs = ver_add[1];
	ver_line= ver_cs * 0x10 + ver_offset;
	disk_add = (unsigned char *)ver_line; 

	cyl =  *((unsigned short *)&disk_add[0]);
	head = disk_add[2];
	sec = disk_add[14];
	precomp = (*(unsigned short *)((&disk_add[5]))) >> 2;
}

static void print_model_number(unsigned char * id)
{
	int i;

	for(i = 27; i < 47; i++)
		printk("%c%c", id[i * 2 + 1], id[i * 2]);
	printk("\n");
}

static void get_hd_info(void)
{
	int i;
	unsigned char id[512]; 
	unsigned int lba_capacity;
	struct partition * par_ptr;

#define CAPACITY_OFF	120

        outb(0xa0, IDE_CURRENT);
        outb(ATA_IDENTIFY, IDE_STATUS);
        ide_status_ok();
	ide_io_read(IDE_DATA, id, 256);

	printk("IDE0-Master: ");
	print_model_number(id);

	lba_capacity = *((unsigned int *)&id[CAPACITY_OFF]);

	/* lba mode, compute again */
	cyl = lba_capacity / (head * sec);
	printk("Your disk C:%d, H:%d, S:%d, PRECOMP:%d, LBA:%d\n", 
			cyl, head, sec, precomp, lba_capacity);

	ide_cmd(0, READ_CMD, 0, 1, TRUE);

	ide_status_ok();
	ide_io_read(IDE_DATA, id, 256);

#if 1
	printk("Partition info:\n");
#endif

	hd_info[0].flag = 1;
	hd_info[0].start_sec = 0;
	hd_info[0].num_sec = lba_capacity;

	par_ptr = (struct partition *)&id[0x1be];
	for(i = 1; i < 5; i++, par_ptr++){
		if(par_ptr->sys_ind == 0){
			hd_info[i].flag = 0;
			continue;
		}

		hd_info[i].flag = 1;
		hd_info[i].start_sec = par_ptr->start_sec;
		hd_info[i].num_sec = par_ptr->num_sec;
#if 1 
		printk("%d: %x %d %dM\n", i, par_ptr->sys_ind,
			par_ptr->start_sec, par_ptr->num_sec / 2048);
#endif
	}
}

static void get_cdrom_info(void)
{
	unsigned char id[512];

	memset(&cdrom_info, 0, sizeof(struct cdrom_info) * 2);

	// identify
	outb(0xe0 | (1 << 4), IDE_CURRENT);
	outb(ATAPI_IDENTIFY, IDE_STATUS);

	cdrom_status_ok();
	if(!cdrom_ready())
		return;

	ide_io_read(IDE_DATA, id, 256);

	printk("IDE0-Slave: ");
	print_model_number(id);

	cdrom_info[0].valid = 1;
	cdrom_info[0].reset = 0;
}

extern int pci_ide_detect(void);

void ide_init(void)
{
	int i;
	extern unsigned char cfgspace[256];

	if(pci_ide_detect()){
		if(cfgspace[0x09] & 0x80){ // programming interface
			dma_ide_flag = 1;
			dma_base_addr
				= *(unsigned long *)&cfgspace[0x20] & (~1UL);

			printk("DMA support, base addr: 0x%08x\n",
				dma_base_addr);

			ide_dma_buffer = (unsigned char *)get_one_page();
			if(!ide_dma_buffer)
				DIE("bug, cannot happen\n");

			ide_prd = (ide_prd_t *)(ide_dma_buffer + 2048);
		}
	}

	list_head_init(&req_freelist);

	for(i = 0; i < REQQ_SIZE - 1; i++)
		list_add_head(&req_freelist, &reqq[i].list);

	set_bottom(IDE_B, ide_bottom);
	put_irq_handler(0x0e, ide_intr);

	/* outb only for not causing intr in vmware */
	//outb(inb(0x21) | 0x04, 0x21);
	outb(inb(0xa1) | 0x40, 0xa1);

	get_hd_info();
	get_cdrom_info();

	//outb(inb(0x21) & 0xfb, 0x21);
	outb(inb(0xa1) & 0xbf, 0xa1);
}
